package org.duang.http.common;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import org.apache.http.HttpHeaders;
import org.apache.http.entity.AbstractHttpEntity;
import org.apache.http.entity.BasicHttpEntity;
import org.duang.http.core.ServiceClient.Request;

public class RepeatableInputStreamEntity extends BasicHttpEntity {

	private NoAutoClosedInputStreamEntity innerEntity;
	private InputStream instream;
	private long isLength;
	private boolean firstAttempt = true;

	public RepeatableInputStreamEntity(Request request) {
		setChunked(false);
		String contentType = request.getHeaders().get(HttpHeaders.CONTENT_TYPE);		
		instream = request.getContent();
		isLength = request.getContentLength();
		innerEntity = new NoAutoClosedInputStreamEntity(instream, isLength);
		innerEntity.setContentType(contentType);
		setContent(instream);
		setContentType(contentType);
		setContentLength(isLength);
	}

	public boolean isChunked() {
		return false;
	}

	public boolean isRepeatable() {
		return instream.markSupported() || innerEntity.isRepeatable();
	}

	public void writeTo(OutputStream output) throws IOException {
		if(!firstAttempt && isRepeatable()){
			instream.reset();
		}
		firstAttempt = false;
		innerEntity.writeTo(output);
	}

	public static class NoAutoClosedInputStreamEntity extends AbstractHttpEntity {
		private final static int BUFFER_SIZE = 2048;
		private final InputStream content;
		private final long length;

		public NoAutoClosedInputStreamEntity(final InputStream content, long length) {
			super();
			if (null == content) {
				throw new NullPointerException("source input stream is null");
			}
			this.content = content;
			this.length = length;
		}

		@Override
		public boolean isRepeatable() {
			return false;
		}

		@Override
		public long getContentLength() {
			return this.length;
		}

		@Override
		public InputStream getContent() throws IOException, UnsupportedOperationException {
			return this.content;
		}

		@Override
		public void writeTo(final OutputStream outstream) throws IOException {
			if (null == outstream) {
				throw new NullPointerException("source out stream is null");
			}
			InputStream inputstream = this.content;
			byte[] buffer = new byte[BUFFER_SIZE];
			int len = 0;
			if (this.length < 0) {
				while ((len = inputstream.read(buffer)) != -1) {
					outstream.write(buffer, 0, len);
				}
			} else {
				long remaining = this.length;
				while(remaining > 0){
					len = inputstream.read(buffer, 0, (int)Math.min(BUFFER_SIZE, remaining));
					if(len == -1) break;
					outstream.write(buffer, 0, len);
					remaining -= len;
				}
			}
		}

		@Override
		public boolean isStreaming() {
			return true;
		}

	}

}
